﻿using Caliburn.Micro;
using Common.UI.Controls;
using Common.Utils;
using Domain;
using Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Common.Extensions;
using LocalSettings;
using System.Xml.Linq;
using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.Storage.Streams;
using System.IO;

namespace GYMm.UI.ViewModels
{
	public class DashboardViewModel : Screen
	{
        private bool _firstStart;
        private Guid? _lastNavigatedExerciseId;        
		private Models.GymModel _model;

		private ObservableCollection<ExerciseItemViewModel> _items;
		public ObservableCollection<ExerciseItemViewModel> Items
		{
			get { return _items; }
			set
			{
				if (_items != value)
				{
					_items = value;
					NotifyOfPropertyChange(() => Items);
				}
			}
		}

		private ExerciseItemViewModel _selectedExercise;
		public ExerciseItemViewModel SelectedExercise
		{
			get { return _selectedExercise; }
			set
			{
				if (_selectedExercise != value)
				{
					_selectedExercise = value;
					NotifyOfPropertyChange(() => SelectedExercise);
				}
			}
		}

		private Boolean _showProgress = false;
		public Boolean ShowProgress
		{
			get { return _showProgress; }
			set
			{
				if (_showProgress != value)
				{
					_showProgress = value;
					NotifyOfPropertyChange(() => ShowProgress);
				}
			}
		}

		private Boolean _noExercises;
		public Boolean NoExercises
		{
			get { return _noExercises; }
			set
			{
				if (_noExercises != value)
				{
					_noExercises = value;
					NotifyOfPropertyChange(() => NoExercises);
				}


			}
		}

		private Boolean _editExerciseMode;        
		public Boolean EditExerciseMode
		{
			get { return _editExerciseMode; }
			set
			{
				if (_editExerciseMode != value)
				{
					_editExerciseMode = value;
					NotifyOfPropertyChange(() => EditExerciseMode);
				}
			}
		}

		public DashboardViewModel(Models.GymModel model)
		{
			_model = model;
		}

        protected override async void OnInitialize()
        {
            base.OnInitialize();

            await InitAsync();
        }

		protected override async void OnActivate()
		{
			base.OnActivate();

            if (!_firstStart)
                await UpdateAsync();

            _firstStart = false;

            await TimeForRate();
		}

        private static async Task TimeForRate()
        {
            if (GYMSettingsStorage.RateDialogIsShown && GYMSettingsStorage.IncRateAttemptCount() == GYMSettingsStorage.LaunchCountForRate)
            {
                var md = new MessageDialog("If you enjoy using GYM Guru, please take a moment to rate the app? Thank for your support!", "Rate GYM Guru");

                md.Commands.Add(new UICommand("Rate Now", async x =>
                {
                    GYMSettingsStorage.RateDialogIsShown = true;
                    var uri = new Uri("ms-windows-store:reviewapp?appid=" + "d886a36a-20b1-4581-992e-11f03f589e98");
                    await Windows.System.Launcher.LaunchUriAsync(uri);
                }));

                md.Commands.Add(new UICommand("Later", x =>
                {
                    GYMSettingsStorage.IncLaunchCountForRate();
                }));

                await md.ShowAsync();
            }
        }

        private async Task UpdateAsync()
        {
            if (!_lastNavigatedExerciseId.HasValue)
                return;

            var lookup = await _model.LoadLookupAsync(_lastNavigatedExerciseId.Value);
            if (lookup != null)
            {
                var item = Items.FirstOrDefault(x => x.ExerciseId == lookup.ExerciseId);
                ToExerciseItemViewModel(lookup, item);
            }
        }

		private async Task InitAsync()
		{
			var loadedLookups = await _model.LoadAllLookupsAsync();

            var items = ApplyOrder(loadedLookups).Select(x => ToExerciseItemViewModel(x));

			Items = new ObservableCollection<ExerciseItemViewModel>(items);
			Items.CollectionChanged += Items_CollectionChanged;
			UpdateNoExercises();
		}

        private static IEnumerable<ExerciseLookup> ApplyOrder(List<ExerciseLookup> loadedLookups)
        {
            var lookupsOrderAndSize = (LocalSettings.GYMSettingsStorage.LoadOrder() ?? new List<Guid>()).Select((x, i) => new { Index = i, ExerciseId = x });
            if (lookupsOrderAndSize.IsNullOrEmpty())
                return loadedLookups;

            var ordered = loadedLookups
                .Join(lookupsOrderAndSize, x => x.ExerciseId, x => x.ExerciseId, (x, y) => new { Index = y.Index, ExerciseLookup = x })
                .OrderBy(x => x.Index).Select(x => x.ExerciseLookup);
            return ordered;
        }

        private static ExerciseItemViewModel ToExerciseItemViewModel(ExerciseLookup lookup, ExerciseItemViewModel itemViewModel = null)
        {         
            var res = itemViewModel ?? new ExerciseItemViewModel();
            
            res.ExerciseId = lookup.ExerciseId;
            res.Date = lookup.Date;
            res.ExerciseDisplayName = lookup.DisplayName;
            res.MaxWeight = lookup.RecordWeight;
            res.Weight = lookup.MaxWeight;
            res.IsCupDisplayed = Math.Abs(lookup.RecordWeight - lookup.MaxWeight) < 0.01
            && (DateTimeHelper.Now - lookup.Date).TotalDays < 2;
            res.IsEmpty = lookup.MaxWeight < 0.01 && lookup.RecordWeight < 0.01;
            res.IsRecordDisplayed = Math.Abs(lookup.RecordWeight - lookup.MaxWeight) > 0.01;
            
            return res;
        }

		void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			SaveItemsOrder();
		}

		private void UpdateNoExercises()
		{
			NoExercises = Items == null || Items.Count == 0;
		}

		public async void NavigateToExerciseDetails(ExerciseItemViewModel item)
		{
			ShowProgress = true;
			var id = (item as ExerciseItemViewModel).ExerciseId;
            _lastNavigatedExerciseId = id;
			var exercise = await _model.LoadExerciseByIdAsync(id);
			ShowProgress = false;
			IoC.Get<INavigationService>().NavigateToViewModel<ExerciseViewModel>(exercise);
		}

		public async Task CreateNewExercise()
		{
			var exercise = new Exercise() { ExerciseLogs = new List<ExerciseLog>() };
			await _model.SaveExerciseAsync(exercise);

			var exerciseItem = new ExerciseItemViewModel()
			{
				ExerciseId = exercise.Id,
				ExerciseDisplayName = exercise.DisplayName,
				IsEmpty = true,
			};
			Items.Add(exerciseItem);
			UpdateNoExercises();
            SelectedExercise = exerciseItem;

			//ShowExerciseEditDialog(exerciseItem);
		}

		public async Task RemoveExerciseAsync()
		{
			if (SelectedExercise.IsEmpty)
			{
				await RemoveExercise();
			}
			else
			{
				await MessageDialogHelper.ShowDialogAsync("CONFIRMATION", "Do you really want to remove the exercise?",
					new UICommand("Remove", x =>
					{
						var t = RemoveExercise();
					}));
			}
		}

		private async Task RemoveExercise()
		{
			await _model.RemoveExerciseAsync(SelectedExercise.ExerciseId);
			Items.Remove(Items.First(x => x.ExerciseId == SelectedExercise.ExerciseId));

			HideExerciseEditDialog();
			UpdateNoExercises();
		}

		public async Task UpdateExerciseDisplayNameAsync()
		{
			await _model.SaveExerciseDisplayNameAsync(SelectedExercise.ExerciseId, SelectedExercise.ExerciseDisplayName);
			HideExerciseEditDialog();
			//SaveItemsOrder();
		}

		public void HideExerciseEditDialog()
		{
			SelectedExercise = null;
			EditExerciseMode = false;
		}

		public void ShowExerciseEditDialog(ExerciseItemViewModel item)
		{
			SelectedExercise = item;
			EditExerciseMode = true;
		}

		protected override void OnDeactivate(bool close)
		{
			base.OnDeactivate(close);
		}

		public void SaveItemsOrder()
		{
			GYMSettingsStorage.SaveOrder(Items.Select(x => x.ExerciseId));
		}

        public async Task SaveAll()
        {
            var exercises = await _model.LoadAllAsync();
            var xDoc = XmlModel.ToXDoc(exercises);

            //TODO: save to file or send by email
            //var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
            //var sampleFile = await folder.CreateFileAsync("db.xml", CreationCollisionOption.ReplaceExisting);
            ////var s = await sampleFile.OpenAsync(FileAccessMode.ReadWrite);
            //await Windows.Storage.FileIO.WriteTextAsync(sampleFile, xDoc.ToString());            
        }

        public async Task SendDbToEmail()
        {
            try
            {
                var exercises = await _model.LoadAllAsync();
                var xDoc = XmlModel.ToXDoc(exercises);

                var mras = new InMemoryRandomAccessStream();
                xDoc.Save(mras.AsStream());
                await Utils.EmailUtils.ShowComposeNewEmailAsync(mras, "DB.xml", String.Format("This is your exercises database on {0}.", DateTimeHelper.Now), "artemsubbotinsamara@gmail.com");
                mras.Dispose();
            }
            catch (Exception ex)
            {
                var md = new MessageDialog(ex.Message, "Some problems occured during sending");
                var t = md.ShowAsync();
            }
        }
    }

	public class ExerciseItemViewModel : PropertyChangedBase
	{
		public Guid ExerciseId { get; set; }

		private String _exerciseDisplayName;
		public String ExerciseDisplayName
		{
			get { return _exerciseDisplayName; }
			set
			{
				if (_exerciseDisplayName != value)
				{
					_exerciseDisplayName = value;
					NotifyOfPropertyChange(() => ExerciseDisplayName);
				}
			}
		}

		private Boolean _isRecordDisplayed;
		public Boolean IsRecordDisplayed
		{
			get { return _isRecordDisplayed; }
			set
			{
				if (_isRecordDisplayed != value)
				{
					_isRecordDisplayed = value;
					NotifyOfPropertyChange(() => IsRecordDisplayed);
				}
			}
		}
		
		private Boolean _isCupDisplayed;
		public Boolean IsCupDisplayed
		{
			get { return _isCupDisplayed; }
			set
			{
				if (_isCupDisplayed != value)
				{
					_isCupDisplayed = value;
					NotifyOfPropertyChange(() => IsCupDisplayed);
				}
			}
		}

		private Double _weight;
		public Double Weight
		{
			get { return _weight; }
			set
			{
				if (_weight != value)
				{
					_weight = value;
					NotifyOfPropertyChange(() => Weight);
				}
			}
		}

		private Double _maxWeight;
		public Double MaxWeight
		{
			get { return _maxWeight; }
			set
			{
				if (_maxWeight != value)
				{
					_maxWeight = value;
					NotifyOfPropertyChange(() => MaxWeight);
				}
			}
		}

		private DateTime _date;
		public DateTime Date
		{
			get { return _date; }
			set
			{
				if (_date != value)
				{
					_date = value;
					NotifyOfPropertyChange(() => Date);
				}
			}
		}

		private Boolean _isEmpty;
		public Boolean IsEmpty
		{
			get { return _isEmpty; }
			set
			{
				if (_isEmpty != value)
				{
					_isEmpty = value;
					NotifyOfPropertyChange(() => IsEmpty);
				}
			}
		}
	}
}
